package edu.asu.obsolete;

import java.util.Arrays;

import edu.asu.sapa.lifted.Symbol;
import edu.asu.sapa.utils.Utility;

public class CostSymbol<T> extends Symbol<T> {

	private static final int DEFAULT = 30;

	int size=0;
	float[] times;
	float[] costs;
	int[] supportIDs;
	
	// should keep track of supporters too, and realize when they drop to 0
	// (if we want to do eager analysis upon no longer applicable actions)
	// the other possibility is to just clear out all propositions and do
	// a re-propagation from the (new) initial action
	public int[] dependents = new int[8];
	public int depSize = 0;

	public int[] supporters = new int[8];
	public int supSize = 0;
	
	public int[] negaters = new int[8];
	public int negSize = 0;

	public CostSymbol(T name) {
		super(name);
		times = new float[DEFAULT];
		costs = new float[DEFAULT];
		supportIDs = new int[DEFAULT];
	}

	public boolean achievable(float time) {
		if (size < 1) {
			return false;
		}

		if (times[0] > time) {
			return false;
		}

		return true;
	}

	/* Manage the costs & supporting action */
	public boolean addCost(float time, float c, int aID) {
		if (size >= costs.length) {
			int capacity = size + (size >> 1) + 1;
			costs = Arrays.copyOf(costs, capacity);
			supportIDs = Arrays.copyOf(supportIDs, capacity);
			times = Arrays.copyOf(times, capacity);
		}

		int i;
		for (i = size - 1; i >= 0; --i) {
			if (times[i] < time) {
				if (c >= costs[i]) {
					return false;
				}
				++i;
				if (i == size) {
					times[size] = time;
					costs[size] = c;
					supportIDs[size] = aID;
					++size;
					return true;
				}
				if (c == costs[i]) {
					if (time == times[i]) {
						return false;
					}
					times[i] = time;
					supportIDs[i] = aID;
				} else if (c > costs[i]) {
					if (time == times[i]) {
						return false;
					}
					Utility.insert(costs, i, c);
					Utility.insert(times, i, time);
					Utility.insert(supportIDs, i, aID);
					++size;
				} else if (c < costs[i]) {
					times[i] = time;
					costs[i] = c;
					supportIDs[i] = aID;
					++i;
					int j;
					for (j = i; j < size; ++j) {
						if (c >= costs[j]) {
							break;
						}
					}
					if (j > i) {
						Utility.remove(costs, i, j - i);
						Utility.remove(times, i, j - i);
						Utility.remove(supportIDs, i, j - i);
						size -= j - i;
					}
				}
				return true;
			}
		}
		++i;
		if (i == size) {
			times[size] = time;
			costs[size] = c;
			supportIDs[size] = aID;
			++size;
			return true;
		}
		if (c == costs[i]) {
			if (time == times[i]) {
				return false;
			}
			times[i] = time;
			supportIDs[i] = aID;
		} else if (c > costs[i]) {
			if (time == times[i]) {
				return false;
			}
			Utility.insert(costs, i, c);
			Utility.insert(times, i, time);
			Utility.insert(supportIDs, i, aID);
			++size;
		} else if (c < costs[i]) {
			times[i] = time;
			costs[i] = c;
			supportIDs[i] = aID;
			++i;
			int j;
			for (j = i; j < size; ++j) {
				if (c >= costs[j]) {
					break;
				}
			}
			if (j > i) {
				Utility.remove(costs, i, j - i);
				Utility.remove(times, i, j - i);
				Utility.remove(supportIDs, i, j - i);
				size -= j - i;
			}
		}
		return true;
	}

	public void deactivate() {
		size = 0;
	}

	public float getCost() {
		return size > 0 ? costs[size - 1] : Float.POSITIVE_INFINITY;
	}

	public float getCost(float time) {
		for (int i = size - 1; i >= 0; --i) {
			if (times[i] <= time) {
				return costs[i];
			}
		}

		return Float.POSITIVE_INFINITY;
	}

	/**
	 * \argmin_t cost(t) == cost(tmax)
	 * 
	 * (\forall t getCost(getMinTime(t)) == getCost(t))
	 */
	public float getMinTime(float tmax) {
		for (int i = size - 1; i >= 0; --i) {
			if (times[i] <= tmax)
				return times[i];
		}

		return Float.NEGATIVE_INFINITY;
	}

	public int getSupport() {
		if (size <= 0) {
			return -1;
		}
		return supportIDs[size - 1];
	}

	public int getSupport(float time) {
		for (int i = size - 1; i >= 0; --i) {
			if (times[i] <= time) {
				return supportIDs[i];
			}
		}

		return Integer.MIN_VALUE;
	}

	public boolean isIn() {
		return size > 0;
	}
	
	public int addDependent(int id) {
		if (depSize >= dependents.length) {
			dependents = Arrays.copyOf(dependents, depSize + 8 + (depSize >> 2));
		} else if (depSize > 0 && dependents[depSize-1] == id) {
			return depSize;
		}
		dependents[depSize++] = id;
		return depSize;
	}
	
	public int addSupporter(int id) {
		if (supSize >= supporters.length) {
			supporters = Arrays.copyOf(supporters, supSize + 8 + (supSize >> 2));
		} else if (supSize > 0 && supporters[supSize-1] == id) {
			return supSize;
		}
		supporters[supSize++] = id;
		return supSize;
	}

	public int addNegater(int id) {
		if (negSize >= negaters.length) {
			negaters = Arrays.copyOf(negaters, negSize + 8 + (negSize >> 2));
		} else if (negSize > 0 && negaters[negSize-1] == id) {
			return negSize;
		}
		negaters[negSize++] = id;
		return negSize;
	}
	
	public void trim() {
		if (depSize != dependents.length)
			dependents = Arrays.copyOf(dependents, depSize);
		if (supSize != supporters.length)
			supporters = Arrays.copyOf(supporters, supSize);
		if (negSize != negaters.length)
			negaters = Arrays.copyOf(negaters, negSize);
	}

	public int removeDependent(int id) {
		// should only be present one time
		for (int i = 0; i < depSize; ++i) {
			if (dependents[i] == id) {
				Utility.remove(dependents, i);
				--depSize;
				--i;
				break;
			}
		}
		return depSize;
	}
	
	public int removeSupporter(int id) {
		// should only be present one time
		for (int i = 0; i < supSize; ++i) {
			if (supporters[i] == id) {
				Utility.remove(supporters, i);
				--supSize;
				--i;
				break;
			}
		}
		return supSize;
	}
	
	public int removeNegater(int id) {
		// should only be present one time
		for (int i = 0; i < negSize; ++i) {
			if (negaters[i] == id) {
				Utility.remove(negaters, i);
				--negSize;
				--i;
				break;
			}
		}
		return negSize;
	}

	public boolean supports(int id) {
		for (int i = depSize-1;i>=0; --i) {
			if (dependents[i] == id)
				return true;
		}
		return false;
	}
	public boolean supportedBy(int id) {
		for (int i = supSize-1;i>=0; --i) {
			if (supporters[i] == id)
				return true;
		}
		return false;
	}
	public boolean negatedBy(int id) {
		for (int i = negSize-1;i>=0; --i) {
			if (negaters[i] == id)
				return true;
		}
		return false;
	}

}
